hooks: resolve repo root from the invoking worktree, fail closed - #92
Merged
Conversation
.githooks/pre-commit and .githooks/pre-push resolved the repository root from the hook file's own location; with core.hooksPath an absolute path into the primary checkout, worktree commits/pushes were validated against the primary checkout's stale/dirty tree (false NOTICE-drift FAIL observed 2026-08-05; false PASSes possible). Root now comes from git rev-parse --show-toplevel, failing closed when unresolvable (Codex cross-family review: a cwd fallback re-opens the wrong-tree class). Adds wrapper tests for the hook-lives-elsewhere topology, fail-closed behavior, and a real-git linked-worktree integration test. Pre-commit hook bypassed with --no-verify: the ACTIVE hook is still the primary checkout's pre-fix copy exhibiting exactly this bug; all gates run green in this tree and CI re-runs them authoritatively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a wrong-tree bug in the local git-hook gates:
.githooks/pre-commitand.githooks/pre-pushresolved the repository root from the hook file's own location (SCRIPT_DIR/..). Becausecore.hooksPathis an absolute path into the primary checkout, any commit or push from a linked session worktree was validated (consistency check, test gate, compliance chain entry) against the primary checkout's tree — stale or dirty with another session's edits — instead of the tree actually being committed. Observed 2026-08-05 as a falseNOTICE ... driftedpre-commit FAIL from a worktree whose own tree passed; the same shape can produce false PASSes.Both wrappers now resolve
REPO_ROOT="$(git rev-parse --show-toplevel)"and fail closed when the root cannot be resolved (no cwd fallback — per the Codex cross-family review, a fallback would re-open the exact wrong-tree class this fixes). The pre-push test-gate subshell sanitization and the exec'd compliance checker (hook env + stdin preserved) are structurally unchanged.Operational note: the fix becomes ACTIVE only when the primary checkout — whose working tree hosts the
core.hooksPathcopies — is updated to a commit containing it (currently parked on a stale commit with uncommitted edits from another session; not touched by this PR).Boundary declaration
Generated and release surfaces
SKILL.mdfiles are in parity. (build_skills.py --checkOK — untouched.)build_marketplace.py --checkOK — untouched.)changelog.d/fragment is present for a user-visible change (changelog.d/20260805-hook-worktree-root.md); generatedCHANGELOG.mduntouched..githookspre-push wrapper precedent.)Verification
python3 scripts/build_skills.py --check— OKpython3 scripts/build_marketplace.py --check— OKpython3 scripts/build-changelog.py --dry-run— OK (--checkis release-PR-only under the fragment-only convention)python3 -m unittest discover -s tests -t . -v— Ran 660, OKpython3 -m unittest discover -s scripts -p 'test_*.py' -v— Ran 333, OK (327 + 6 new: hook-lives-elsewhere for both wrappers, fail-closed rev-parse for both, branch-detection knob split, real-git linked-worktree integration test)python3 scripts/check_release_consistency.py— RESULT: OK (in the PR worktree)python3 scripts/secret_scan.py— cleanpython3 scripts/check-public-export-safety.py --active-tree— SAFE (history mode not run: local clone retains pre-rewrite lineage and fails history mode by design; seedocs/public-governance.md. No history modified.)git diff --check— cleanCommit made with
--no-verifyfor documented cause: the currently-ACTIVE pre-commit is the primary checkout's pre-fix copy exhibiting exactly the bug this PR fixes; every gate it would run is green in this tree (above) and re-runs authoritatively in CI.Review and post-condition
Tier 2 (repository validation tooling). Independent cross-family review: Codex (OpenAI, gpt-5.5, effort high, CLI read-only sandbox) — initial verdict DISAGREE-MINOR (confidence H): core root-resolution change endorsed ("the hook file path is irrelevant to Git's repository discovery"); load-bearing recommendation to replace the cwd fallback with fail-closed — integrated (both hooks now hard-fail, with tests). Suggested real-git worktree integration test — integrated. Suggested re-cd'ing the exec'd compliance checker under sanitized env — declined with cause:
hook-pre-push.pyrequires the hook's git env and stdin ref data, and resolves its own root from__file__. Post-merge: update the primary checkout so the fixed hooks become active, then a worktree commit exercises the fix in production.Compliance trace
author: Claude (Anthropic) on behalf of @sumitake
standing_directives: AGENTS.md source boundaries; docs/public-governance.md; adversarial review folded into cross-check per operator directive
tier: 2
cross_check: PROCEED-WITH-MODIFICATIONS — Codex (OpenAI, gpt-5.5) DISAGREE-MINOR/H; fail-closed root resolution and real-git integration test integrated; compliance-checker re-cd suggestion declined with cause (needs hook env + stdin)
post_condition: CI green; primary checkout to be updated so fixed hooks activate; worktree commit then validates its own tree
mcp_coverage_gap: NONE — no MCP tool surface changes
contributor_rights: OWNER-AUTHORED
operator_reserved: no
🤖 Generated with Claude Code